package com.spzx.common.redis.configure;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import jakarta.annotation.Resource;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.time.Duration;

/**
 * redis配置
 *
 * @author spzx
 */
@Configuration
@EnableCaching
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport
{
    @Resource
    RedisProperties redisProperties;
    @Bean
    public ObjectMapper objectMapper() {
        // objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        ObjectMapper mapper = Jackson2ObjectMapperBuilder.json()
                .featuresToDisable(SerializationFeature.WRITE_NULL_MAP_VALUES)
                .build();
        //修改ObjectMapper 对象转换处理器
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),ObjectMapper.DefaultTyping.NON_FINAL);
        return mapper;
//        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),ObjectMapper.DefaultTyping.NON_FINAL);
    }
    //向容器注入 RedisClient对象
    @Bean
    public RedissonClient redisson(){
        Config config = new Config();
        config.useSingleServer() //连接单机redis
                .setAddress("redis://"+redisProperties.getHost()+":"+redisProperties.getPort()) //redis地址
                .setTimeout(60000); //过期时间

        //1、创建redisson的客户端对象:  参数：redis的配置对象
        return Redisson.create(config);
    }

    //注册redis自动缓存管理器到容器中
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory){
        //RedisCacheManager的配置类对象
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //缓存穿透(访问的数据数据库一定不存在，每次请求都会先经过缓存 再经过数据库处理)
//                .disableCachingNullValues() 禁止缓存空值，可以缓存空值，避免恶意访问
                .entryTtl(Duration.ofSeconds(600000)) //自动缓存的过期时间
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer())) //缓存时键的序列化器
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer( new GenericJackson2JsonRedisSerializer())) //值的序列化器：使用json序列化器
                ;
        //通过RedisCacheManager的构建方法创建缓存管理器  并配置
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .build();
    }
    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
//        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
}
